package org.wzl.frontendtest.core.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.wzl.frontendtest.core.FrontComponentAnalyze;
import org.wzl.frontendtest.core.bo.FrontCodeFileRelation;
import org.wzl.frontendtest.exception.FrontComponentAnalyzeException;
import org.wzl.frontendtest.exception.FrontPreException;

import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
public class FrontComponentAnalyzeImpl implements FrontComponentAnalyze {

    private static final String PACKAGE_JSON = "package.json";

    private static final String PROJECT_ONE_LEVEL_FOLDER = "src";

    private static final String PROJECT_TWO_LEVEL_FOLDER = "pages";

    /**
     * 项目汇总包含src的目录
     */
    private File srcFolder;

    /**
     * 代码文件名与具体的文件映射
     * 例如：
     *   "src/pages/src/common/user-trace.jsx" -> File(D:\gitlab\yinhe\src\component\\user-trace.jsx)
     */
    private Map<String, File> codeFileNameMap;

    @Override
    public List<FrontCodeFileRelation> analyze(String filePath) {
        File file = new File(filePath);
        File[] files = file.listFiles();

        log.info("校验前端工程文件");
        validFrontProject(files);
        log.info("校验前端工程文件完毕");

        preVarPost(files);

        log.info("开始分析前端工程文件");
//        doAnalyze(files);
        return doAnalyzeV1();
    }

    private List<FrontCodeFileRelation> doAnalyzeV1() {
        List<FrontCodeFileRelation> ans = new ArrayList<>();
        for (String filePathName : codeFileNameMap.keySet()) {
            File file = codeFileNameMap.get(filePathName);
            FrontCodeFileRelation frontCodeFileRelation = getFrontCodeFileRelation(file);
            ans.add(frontCodeFileRelation);
        }
        return ans;
    }

    private FrontCodeFileRelation getFrontCodeFileRelation(File targetFile) {
        List<String> importFiles = analyzeSingleFile(targetFile);

        FrontCodeFileRelation frontCodeFileRelation = new FrontCodeFileRelation();
        frontCodeFileRelation.setFileName(targetFile.getName());
        frontCodeFileRelation.setFilePath(targetFile.getPath());
        frontCodeFileRelation.setRelations(getImportFilePath(importFiles, targetFile));
        frontCodeFileRelation.setFile(targetFile);
        return frontCodeFileRelation;
    }

    private void preVarPost(File[] files) {
        for (File file : files) {
            if (file.getName().equals(PROJECT_ONE_LEVEL_FOLDER)) {
                srcFolder = file;
                break;
            }
        }

        if (null == srcFolder) {
            throw new FrontPreException("没有src文件夹");
        }

        codeFileNameMap = new HashMap<>();
        dfsCodeFileNameMap(srcFolder);
    }

    private void dfsCodeFileNameMap(File file) {
        if (null == file) {
            return;
        }
        if (file.isDirectory()) {
            File[] files1 = file.listFiles();
            if (null == files1) {
                return;
            }
            for (File file1 : files1) {
                dfsCodeFileNameMap(file1);
            }
        } else {
            if (file.getName().endsWith(".jsx")
                    || file.getName().endsWith(".tsx")
                    || file.getName().endsWith(".js")
                    || file.getName().endsWith(".ts")) {
                String pathFromSrc = getPathFromSrc(file);
                codeFileNameMap.put(pathFromSrc, file);
            }
        }
    }

    public static String getPathFromSrc(File file) {
        String fullPath = file.getAbsolutePath().replace("\\", "/"); // 统一分隔符，兼容 Windows
        int index = fullPath.indexOf("/src/");
        if (index == -1) {
            index = fullPath.indexOf("src/");
        }

        if (index != -1) {
            return fullPath.substring(index);
        } else {
            return file.getName(); // fallback: 只返回文件名
        }
    }


    /**
     * 将文件路径从后往前拆分为各个部分
     * @param path 输入的文件路径，例如 "D:\\gitlab\\yinhe\\src\\component\\AnchorTab\\index.jsx"
     * @return 从文件名到根路径的列表，例如 ["index.jsx", "AnchorTab", ..., "D:"]
     */
    public static List<String> splitPathFromTail(String path) {
        File file = new File(path);
        List<String> components = new ArrayList<>();

        while (file != null) {
            String name = file.getName();
            // 根路径（如 D:）的 getName() 会返回空字符串
            if (name.isEmpty()) {
                components.add(file.getPath());
                break;
            } else {
                components.add(name);
            }
            file = file.getParentFile();
        }

        return components;
    }

    /**
     * 获取文件的路径
     * 将jsx/tsx中导入文件的代码，例如：import userTrance form '@/common/user-trace'
     * 转换为：src/pages/src/common/user-trace.jsx
     *
     * @param importFiles jsx/tsx中导入文件的代码，例如：
     * @param currentFile  当前文件类
     * @return 代码的相对路径
     */
    private List<FrontCodeFileRelation> getImportFilePath(List<String> importFiles, File currentFile) {
        List<FrontCodeFileRelation> ans = new ArrayList<>();
        for (String importFile : importFiles) {
            if (importFile.endsWith("scss") || importFile.endsWith("less") || importFile.endsWith("css")) {
                continue;
            }
            if (importFile.startsWith("@")) {
                importFile = importFile.replace("@", srcFolder.getAbsolutePath());
                File targetFile = fillCodeFileSuffix(importFile);
                if (null == targetFile) {
                    continue;
                }

                FrontCodeFileRelation frontCodeFileRelation = new FrontCodeFileRelation();
                frontCodeFileRelation.setFile(targetFile);
                frontCodeFileRelation.setFileName(targetFile.getName());
                frontCodeFileRelation.setFilePath(targetFile.getAbsolutePath());
                ans.add(frontCodeFileRelation);
            }

            if (importFile.startsWith("./")) {
                importFile = (currentFile.getParentFile().getAbsolutePath() + importFile).replace("./", File.separator);
                File targetFile = fillCodeFileSuffix(importFile);
                if (null == targetFile) {
                    continue;
                }
                FrontCodeFileRelation frontCodeFileRelation = new FrontCodeFileRelation();
                frontCodeFileRelation.setFile(targetFile);
                frontCodeFileRelation.setFileName(targetFile.getName());
                frontCodeFileRelation.setFilePath(targetFile.getAbsolutePath());
                ans.add(frontCodeFileRelation);
            }

            //TODO
            if (importFile.startsWith("../")) {

            }


            FrontCodeFileRelation frontCodeFileRelation = new FrontCodeFileRelation();
            File targetFile = findCodeFile(importFile);
            if (null != targetFile) {
                frontCodeFileRelation.setFilePath(targetFile.getPath());
                frontCodeFileRelation.setFileName(targetFile.getName());
                ans.add(frontCodeFileRelation);
            }
        }
        return ans;
    }

    /**
     * 给代码路径字符串添加后缀名，使其变成一个合法的File文件
     * @param importFile 例如：@/common/sto-env
     * @return D://gitlab/yinhe/src/common/sto-env.js
     */
    private File fillCodeFileSuffix(String importFile) {
        List<String> fileSuffix = List.of(".js", ".jsx", ".ts", ".tsx", File.separator + "index.jsx",File.separator + "index.tsx");
        for (String suffix : fileSuffix) {
            File file = new File(importFile + suffix);
            if (file.isFile()) {
                return file;
            }
        }
        return null;
    }

    /**
     * 寻找代码文件
     * @param importCode    前端导入的代码路径，例如：@/common/user-trace、../common/user-trace
     * @return  该文件
     */
    private File findCodeFile(String importCode) {
        String[] importCodeSplit = importCode.split("/");
        for (String codeFileName : codeFileNameMap.keySet()) {
            File file = codeFileNameMap.get(codeFileName);
            boolean isMatch = true;
            List<String> codeFileSplit = splitPathFromTail(file.getAbsolutePath());
            for (int i = importCodeSplit.length - 1; i > 0; i--) {
                String s = codeFileSplit.get(importCodeSplit.length - i - 1);
                if (!s.equals(importCodeSplit[i])) {
                    isMatch = false;
                    break;
                }
            }
            if (isMatch) {
                return file;
            }
        }
        return null;
    }

    private List<String> analyzeSingleFile(File srcFile) {
        String content = "";
        try {
            content = new String(Files.readAllBytes(srcFile.toPath()));
        } catch (Exception e) {
            log.error("读取文件失败", e);
        }

        Pattern importPattern = Pattern.compile("import\\s+[^;]+?from\\s+['\"]([^'\"]+)['\"]");
        Matcher matcher = importPattern.matcher(content);

        List<String> externalJsxImports = new ArrayList<>();

        while (matcher.find()) {
            String path = matcher.group(1);
            if (path.startsWith("./") || path.startsWith("../") || path.startsWith("@/")) {
                externalJsxImports.add(path);
            }
        }
        return externalJsxImports;
    }

    /**
     * 校验是否是前端项目
     * @param files 前端工程文件
     */
    private void validFrontProject(File[] files) {

        if (null == files) {
            throw new FrontComponentAnalyzeException("该目录下没有文件");
        }

        boolean hasPackageJson = false;
        for (File targetFile : files) {
            if (targetFile.getName().contains(PACKAGE_JSON)) {
                hasPackageJson = true;
                break;
            }
        }

        if (!hasPackageJson) {
            throw new FrontComponentAnalyzeException("该目录下没有package.json文件");
        }

        boolean hasPagesFolder = isHasPagesFolder(files);

        if (!hasPagesFolder) {
            throw new FrontComponentAnalyzeException("该目录下没有pages文件夹");
        }
    }

    private static boolean isHasPagesFolder(File[] files) {
        boolean hasPagesFolder = false;
        for (File file : files) {
            if (file.getName().contains(PROJECT_ONE_LEVEL_FOLDER)) {
                File[] srcFiles = file.listFiles();
                if (null == srcFiles) {
                    throw new FrontComponentAnalyzeException("该目录下src文件夹下没有文件");
                }
                for (File srcFile : srcFiles) {
                    if (srcFile.getName().equals(PROJECT_TWO_LEVEL_FOLDER)) {
                        hasPagesFolder = true;
                        break;
                    }
                }
            }
        }
        return hasPagesFolder;
    }

}
